package map.core;

import com.google.common.collect.ImmutableList;
import ucar.ma2.Array;
import ucar.ma2.InvalidRangeException;
import ucar.nc2.Dimension;
import ucar.nc2.NetcdfFile;
import ucar.nc2.NetcdfFiles;
import ucar.nc2.Variable;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class NetCDFSource implements AutoCloseable{


    private NetcdfFile ncfile;

    public NetCDFSource(File file) {
        this.ncfile = open(file);
    }


    public NetcdfFile getNcfile() {
        return ncfile;
    }

    public   List<NetCDFLayerBaseInfo> listLayerBaseInfo(){
        List<NetCDFLayerBaseInfo> rs = new ArrayList<>();
        List<Variable> variables = ncfile.getRootGroup().getVariables();
        for (Variable variable : variables) {
            NetCDFLayer meta = getLayer(variable,ncfile);
            if(meta != null && meta.getBaseInfo() != null){
                rs.add(meta.getBaseInfo());
            }
        }
        return  rs;
    }

    public   List<NetCDFLayerBaseInfo> isz(Variable variable){
        List<NetCDFLayerBaseInfo> rs = new ArrayList<>();

        NetCDFLayer meta = getLayer(variable,ncfile);
        System.out.println(meta);
        return  rs;
    }
    public List<NetCDFLayer> listAllLayer(){
        List<NetCDFLayer> layers  = new ArrayList<>();
        for (NetCDFLayerBaseInfo layerBaseInfo : listLayerBaseInfo()) {
            layers.add(this.getLayer(layerBaseInfo.getName()));
        }
        return layers;
    }


    private static  boolean isLonVar(Variable variable){
        if(!variable.getDataType().isNumeric()){
            return false;
        }
       String name = variable.getFullName();

       if(name.equalsIgnoreCase("lon") || name.equalsIgnoreCase("longitude") || name.equalsIgnoreCase("Longitude")){
            return  true;
       }

        return false;
    }

    private static  boolean isLatVar(Variable variable){
        if(!variable.getDataType().isNumeric()){
            return false;
        }
        String name = variable.getFullName();

        if(name.equalsIgnoreCase("lat") || name.equalsIgnoreCase("latitude") || name.equalsIgnoreCase("Latitude")){
            return  true;
        }

        return false;
    }


    private static boolean isZVar(Variable variable){
        if(!variable.getDataType().isNumeric()){
            return false;
        }
        String name = variable.getFullName();

        if(name.equalsIgnoreCase("z")){
            return  true;
        }

        return false;
    }

    private static  boolean isTimeVar(Variable variable){
        if(!variable.getDataType().isNumeric()){
            return false;
        }
        String name = variable.getFullName();

        if(name.equalsIgnoreCase("time")){
            return  true;
        }
        if(name.equalsIgnoreCase("FCST")){
            return  true;
        }

        return false;
    }

    public DoubleRange getLayerValueRange(String name){
        Variable variable = findRequireVariable(ncfile,name);
        try {
            Array array  = variable.read();
            double min = Double.MAX_VALUE;
            double max = Double.MIN_VALUE;

            for(int i = 0 ;i < array.getSize() ; i ++){
                double value = array.getFloat(i);
                if(value < min){
                    min = value;
                }
                if(value > max){
                    max = value;
                }
            }

            return new DoubleRange(min,max);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
    public   NetCDFLayer getLayer(String name){
        Variable variable = findRequireVariable(ncfile,name);
        return getLayer(variable,ncfile);
    }

   static private NetCDFLayer getLayer(Variable variable, NetcdfFile ncfile){
        ImmutableList<Dimension> dimensions = variable.getDimensions();

       Map<String,String> map = new HashMap();
       ImmutableList<Variable> variables = ncfile.getVariables();
       for (int i = 0; i < variables.size(); i++) {
           if(variables.size()>=3){
               if(variables.get(i).getDimensions().size() == 1){
                   String fullName1 = variables.get(i).getFullName();
                   String name = variables.get(i).getDimensions().get(0).getName();
                   map.put(name,fullName1);
               }
           }
       }

       Variable lonVar = null;
       Variable latVar  = null;
       Variable timeVar = null;
       Variable zVar = null;



       for (Dimension dimension : dimensions) {
           Variable dimVariable = ncfile.findVariable(map.get(dimension.getName()));
           if(dimVariable != null){
               if(isLonVar(dimVariable)){
                   lonVar = dimVariable;
               }
               else if(isLatVar(dimVariable)){
                   latVar = dimVariable;
               }
               else if(isTimeVar(dimVariable)){
                   timeVar = dimVariable;
               }
               else if(isZVar(dimVariable)){
                   zVar = dimVariable;
               }
           }
       }

       if(lonVar != null && latVar != null){
           return new NetCDFLayer(variable,lonVar,latVar,zVar,timeVar,ncfile,map);

       }
       else {
           return null;
       }
     }


    private static Variable findRequireVariable(NetcdfFile ncfile, String variableName){
        Variable variable = ncfile.findVariable(variableName);
        if(variable!= null){
            return variable;
        }
        else {
            throw new RuntimeException("无效variableName " + variableName);
        }
    }



    public  static NetcdfFile open(File file){
        try {
            return NetcdfFiles.open(file.getAbsolutePath());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public  void close(){
        try {
            this.ncfile.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    public void export(LonLatBounds bounds,IntRange timeRange){

    }

    /**
     * 获取最大最小值
     * @param name
     * @param emptyData
     * @return
     */
    public DoubleRange getLayerValueRange(String name,float emptyData,int wd) throws InvalidRangeException {
        Variable variable = findRequireVariable(ncfile,name).slice(0,wd);//wd时间维度,将每个时间维度数据最大值最小值筛选出来
        try {
            Array array  = variable.read();
            float min = Float.MAX_VALUE;
            float max = -Float.MIN_VALUE;


            for(int i = 0 ;i < array.getSize() ; i ++){
                float value = array.getFloat(i);

                if(isValid(value,emptyData)){
                    if(value < min){
                        min = value;
                    }
                    if(value > max){
                        max = value;
                    }
                }
            }



            return new DoubleRange(min,max);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    private boolean isValid(float value,float emptyData){
        if(value >= 1.0e30){
            return false;
        }
        if(value == emptyData ){
            return false;
        }
        return true;
    }

}
